package com.fengwk.support.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.function.Void;
import com.fengwk.support.util.LoggerUtils.Logger;

/**
 * 工具类:操作Io流
 */
public final class IoUtils {

	private static final Logger LOG = LoggerUtils.getLogger(IoUtils.class);
	
	public static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
	
	private IoUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 构建Reader
	 * 
	 * @param path
	 * @return
	 */
	public static Reader buildReader(String path) {
		return buildReader(FileUtils.buildFile(path));
	}
	
	/**
	 * 构建Reader
	 * 
	 * @param file
	 * @return
	 */
	public static Reader buildReader(File file) {
		try {
			return new FileReader(file);
		} catch (FileNotFoundException e) {
		    LOG.error(e);
		}
		return null;
	}
	
	/**
	 * 构建Writer
	 * 
	 * @param file
	 * @return
	 */
	public static Writer buildWriter(File file) {
		try {
			return new FileWriter(file);
		} catch (IOException e) {
		    LOG.error(e);
		}
		return null;
	}
	
	/**
	 * 构建Writer
	 * 
	 * @param path
	 * @return
	 */
	public static Writer buildWriter(String path) {
		return buildWriter(FileUtils.buildFile(path, true));
	}
	
	/**
	 * 路径构建输入流,若为classpath前缀则使用系统类加载器加载
	 * 
	 * @param path
	 * @return
	 */
	public static InputStream buildInputStream(String path) {
		return buildInputStream(path, ClassUtils.getDefaultClassLoader());
	}
	
	/**
	 * 路径构建输入流,若为classpath使用指定类加载器加载
	 * 
	 * @param path
	 * @param classLoader
	 * @return
	 */
	public static <T> InputStream buildInputStream(String path, ClassLoader classLoader) {
		if(SystemUtils.isClasspath(path)) {
			return classLoader.getResourceAsStream(StringUtils.trimPrefix(path, SystemUtils.PREFIX_CLASSPATH));
		}
		return buildInputStream(FileUtils.buildFile(path));
	}
	
	/**
	 * 路径构建输入流
	 * 
	 * @param file
	 * @return
	 */
	public static InputStream buildInputStream(File file) {
		FileUtils.assertExists(file);
		FileUtils.auth(file);
		InputStream is = null;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
		    LOG.error(e);
		}
		return is;
	}
	
	/**
	 * 路径构建输出流
	 * 
	 * @param file
	 * @return
	 */
	public static OutputStream buildOutputStream(String path) {
		return buildOutputStream(FileUtils.buildFile(path, true));
	}
	
	/**
	 * 路径构建输出流
	 * 
	 * @param file
	 * @return
	 */
	public static OutputStream buildOutputStream(File file) {
		OutputStream os = null;
		try {
			os = new FileOutputStream(file);
		} catch (FileNotFoundException e) {
		    LOG.error(e);
		}
		return os;
	}
	
	/**
	 * 读入
	 * 
	 * @param filePath
	 * @return
	 */
	public static byte[] readBytes(String path) {
		return readBytes(FileUtils.buildFile(path, false));
	}
	
	/**
	 * 读入
	 * 
	 * @param file
	 * @return
	 */
	public static byte[] readBytes(File file) {
		return toBytes(buildInputStream(file), true);
	}
	
	/**
	 * 读入
	 * 
	 * @param path
	 * @return
	 */
	public static String readString(String path) {
		if(SystemUtils.isClasspath(path)) {
			return toString(buildInputStream(path));
		}
		return readString(FileUtils.buildFile(path, false));
	}
	
	/**
	 * 读入
	 * 
	 * @param file
	 * @return
	 */
	public static String readString(File file) {
		return toString(buildReader(file), true);
	}
	
	/**
	 * 写出
	 * 
	 * @param is
	 * @param path
	 * @return 写出内容bytes
	 */
	public static byte[] write(InputStream is, String path) {
		return write(is, FileUtils.buildFile(path, true));
	}
	
	/**
	 * 写出
	 * 
	 * @param is
	 * @param file
	 * @return 写出内容bytes
	 */
	public static byte[] write(InputStream is, File file) {
		return write(is, buildOutputStream(file));
	}
	
	/**
	 * 写出
	 * 
	 * @param is
	 * @param os
	 * @return 写出内容bytes
	 */
	public static byte[] write(InputStream is, OutputStream os) {
		byte[] bytes = toBytes(is);
		write(bytes, os);
		return bytes;
	}
	
	/**
	 * 写出
	 * 
	 * @param bytes
	 * @param filePath
	 */
	public static void write(byte[] bytes, String path) {
		write(bytes, FileUtils.buildFile(path, true));
	}
	
	/**
	 * 写出
	 * 
	 * @param bytes
	 * @param file
	 */
	public static void write(byte[] bytes, File file) {
		write(bytes, buildOutputStream(file));
	}
	
	/**
	 * 写出
	 * 
	 * @param bytes
	 * @param os
	 */
	public static void write(byte[] bytes, OutputStream os) {
		try {
			os.write(bytes);
		} catch (IOException e) {
		    LOG.error(e);
		} finally {
			close(os);
		}
	}
	
	/**
	 * 写出
	 * 
	 * @param content
	 * @param filePath
	 */
	public static void write(String content, String filePath) {
		write(content, FileUtils.buildFile(filePath, true));
	}
	
	/**
	 * 写出
	 * 
	 * @param content
	 * @param file
	 */
	public static void write(String content, File file) {
		write(content, buildWriter(file));
	}
	
	/**
	 * 写出
	 * 
	 * @param content
	 * @param writer
	 */
	public static void write(String content, Writer writer) {
		try {
			writer.write(content);
		} catch (IOException e) {
		    LOG.error(e);
		} finally {
			close(writer);
		}
	}
	
	/**
	 * String转为Reader
	 * 
	 * @param str
	 * @return
	 */
	public static Reader toReader(String str) {
		return new StringReader(str);
	}
	
	/**
	 * InputStream转为Reader
	 * 
	 * @param is
	 * @return
	 */
	public static Reader toReader(InputStream is) {
		return new InputStreamReader(is);
	}
	
	/**
	 * InputStream转为Reader
	 * 
	 * @param is
	 * @param charset
	 * @return
	 */
	public static Reader toReader(InputStream is, String charset) {
		Reader r = null;
		try {
			r = new InputStreamReader(is, charset);
		} catch (UnsupportedEncodingException e) {
		    LOG.error(e);
		}
		return r;
	}
	
	/**
	 * OutputStream转为Writer
	 * 
	 * @param os
	 * @return
	 */
	public static Writer toWriter(OutputStream os) {
		return new OutputStreamWriter(os);
	}
	
	/**
	 * OutputStream转为Writer
	 * 
	 * @param os
	 * @return
	 */
	public static Writer toWriter(OutputStream os, String charset) {
		Writer w = null;
		try {
			w = new OutputStreamWriter(os, charset);
		} catch (UnsupportedEncodingException e) {
		    LOG.error(e);
		}
		return w;
	}
	
	/**
	 * String转为InputStream
	 * 
	 * @param str
	 * @return
	 */
	public static InputStream toInputStream(String str){
		return toInputStream(str.getBytes());
	}
	
	/**
	 * byte[]转为InputStream
	 * 
	 * @param bytes
	 * @return
	 */
	public static InputStream toInputStream(byte[] bytes){
		return new ByteArrayInputStream(bytes);
	}
	
	/**
	 * OutputStream向byte[]转换
	 * 
	 * @param osFun
	 * @return
	 */
	/*
	public static byte[] toBytes(Void.Params1<OutputStream> osFun) {
		byte[] res = null;
		try {
			final PipedInputStream pis = new PipedInputStream();
			final PipedOutputStream pos = new PipedOutputStream(pis);
			try {
				ThreadUtils.start(new Runnable() {
					@Override
					public void run() {
						osFun.apply(pos);
					}
				});
				res = toBytes(pis);
			} finally {
				close(pos);
				close(pis);
			}
		} catch (IOException e) {
		    LOG.error(e);
		}
		return res;
	}
	*/
	
	/**
	 * OutputStream向byte[]转换
	 * 
	 * @param osFun
	 * @return
	 */
	public static byte[] toBytes(Void.Params1<OutputStream> osFun) {
		ByteArrayOutputStream bao = new ByteArrayOutputStream();
		try {
			osFun.apply(bao);
		} finally {
			close(bao);
		}
		return bao.toByteArray();
	}
	
	/**
	 * InputStream转为byte[]
	 * 
	 * @param is
	 * @param closeIs
	 * @return
	 */
	public static byte[] toBytes(InputStream is, boolean isClose) {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
        try {
			copy(is, output);
		} catch (IOException e) {
		    LOG.error(e);
		} finally {
			if (isClose)
				close(is);
		}
        return output.toByteArray();
	}
	
	/**
	 * InputStream转为byte[]
	 * 
	 * @param is 输入流
	 * return byte[]
	 */
	public static byte[] toBytes(InputStream is) {
        return toBytes(is, false);
	}
	
	/**
	 * Reader转为String
	 * 
	 * @param reader
	 * @param isClose
	 * @return
	 */
	public static String toString(Reader reader, boolean isClose) {
		StringWriter sw = new StringWriter();
        try {
			copy(reader, sw);
		} catch (IOException e) {
		    LOG.error(e);
		} finally {
			if (isClose)
				close(reader);
		}
        return sw.toString();
	}
	
	/**
	 * Reader转为String
	 * 
	 * @param reader
	 * @return
	 */
	public static String toString(Reader reader) {
        return toString(reader, false);
	}
	
	/**
	 * InputStream转为String
	 * 
	 * @param is
	 * @param isClose
	 * @return
	 */
	public static String toString(InputStream is, String encoding, boolean isClose) {
		StringWriter sw = new StringWriter();
        try {
			copy(is, sw, encoding);
		} catch (IOException e) {
		    LOG.error(e);
		} finally {
			if (isClose)
				close(is);
		}
        return sw.toString();
	}
	
	public static String toString(InputStream is, boolean isClose) {
		return toString(is, null, isClose);
	}
	
	/**
	 * InputStream转为String
	 * 
	 * @param is
	 * @return
	 */
	public static String toString(InputStream is) {
        return toString(is, false);
	}
	
	/**
	 * 关闭流
	 * 
	 * @param is
	 */
	public static void close(InputStream is) {
		if(is != null) {
			try {
				is.close();
			} catch (IOException e) {
			    LOG.error("close the InputStream failure [" + is.toString() + "]");
			    LOG.error(e);
			}
		}
	}
	
	/**
	 * 关闭流
	 * 
	 * @param is
	 */
	public static void close(OutputStream os) {
		if(os != null) {
			try {
				os.close();
			} catch (IOException e) {
			    LOG.error("close the OutputStream failure [" + os.toString() + "]");
			    LOG.error(e);
			}
		}
	}
	
	/**
	 * 关闭流
	 * 
	 * @param reader
	 */
	public static void close(Reader reader) {
		if(reader != null) {
			try {
				reader.close();
			} catch (IOException e) {
			    LOG.error("close the Reader failure [" + reader.toString() + "]");
				LOG.error(e);
			}
		}
	}
	
	/**
	 * 关闭流
	 * 
	 * @param writer
	 */
	public static void close(Writer writer) {
		if(writer != null) {
			try {
				writer.close();
			} catch (IOException e) {
			    LOG.error("close the Writer failure [" + writer.toString() + "]");
			    LOG.error(e);
			}
		}
	}
	
	/**
	 * 序列化对象为byte数组
	 * 
	 * @param obj 需要实现序列化接口
	 * @return
	 */
	public static <O extends Serializable> byte[] objectToBytes(O obj) {
		if(obj == null) {
			return null;
		}
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bytes = null;
        ObjectOutputStream oos = null;
        try {
        	oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            bytes = baos.toByteArray();
        } catch (IOException e) {
            LOG.error(e);
        }
        close(oos);
        close(baos);
        return bytes;
	}
	
	/**
	 * 反序列byte数组化为对象
	 * 
	 * @param obj
	 * @return
	 */
	public static Object bytesToObject(byte[] bytes) {
		if(bytes == null) {
			return null;
		}
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream in = null;
        Object obj = null;
        try {
        	in = new ObjectInputStream(bais);
			obj = in.readObject();
		} catch (ClassNotFoundException | IOException e) {
		    LOG.error(e);
		}
        close(in);
        close(bais);
        return obj;
	}
	
	/**
	 * InputStream拷贝至OutputStream
	 * 
	 * @param input
	 * @param output
	 * @return
	 * @throws IOException
	 */
    public static int copy(InputStream input, OutputStream output) throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }
    
    /**
     * InputStream拷贝至OutputStream
     * 
     * @param input
     * @param output
     * @return
     * @throws IOException
     */
    public static long copyLarge(InputStream input, OutputStream output)
            throws IOException {
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }
    
    /**
     * InputStream拷贝至Writer
     * 
     * @param input
     * @param output
     * @throws IOException
     */
    public static void copy(InputStream input, Writer output)
            throws IOException {
        InputStreamReader in = new InputStreamReader(input);
        copy(in, output);
    }

    /**
     * InputStream拷贝至Writer
     * 
     * @param input
     * @param output
     * @param encoding
     * @throws IOException
     */
    public static void copy(InputStream input, Writer output, String encoding)
            throws IOException {
        if (encoding == null) {
            copy(input, output);
        } else {
            InputStreamReader in = new InputStreamReader(input, encoding);
            copy(in, output);
        }
    }

    /**
     * Reader拷贝至Writer
     * 
     * @param input
     * @param output
     * @return
     * @throws IOException
     */
    public static int copy(Reader input, Writer output) throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    /**
     * Reader拷贝至Writer
     * 
     * @param input
     * @param output
     * @return
     * @throws IOException
     */
    public static long copyLarge(Reader input, Writer output) throws IOException {
        char[] buffer = new char[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    /**
     * Reader拷贝至OutputStream
     * 
     * @param input
     * @param output
     * @throws IOException
     */
    public static void copy(Reader input, OutputStream output)
            throws IOException {
        OutputStreamWriter out = new OutputStreamWriter(output);
        copy(input, out);
        out.flush();
    }

    /**
     * Reader拷贝至OutputStream
     * 
     * @param input
     * @param output
     * @param encoding
     * @throws IOException
     */
    public static void copy(Reader input, OutputStream output, String encoding)
            throws IOException {
        if (encoding == null) {
            copy(input, output);
        } else {
            OutputStreamWriter out = new OutputStreamWriter(output, encoding);
            copy(input, out);
            out.flush();
        }
    }
	
}
